// 遵守 ICMP 协议的时间服务器客户端程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/ip_icmp.h>

#define BUFFER_SIZE 1024

unsigned short in_cksum(unsigned short *addr, int len);

// 发送ICMP请求
void send_icmp_request(int sockfd, struct sockaddr_in server_addr) {
    char buffer[BUFFER_SIZE];
    struct icmphdr *icmp_hdr = (struct icmphdr *)buffer;
    icmp_hdr->type = ICMP_ECHO;
    icmp_hdr->code = 0;
    icmp_hdr->checksum = 0;
    icmp_hdr->un.echo.id = getpid();
    icmp_hdr->un.echo.sequence = 1;
    // 计算校验和
    icmp_hdr->checksum = in_cksum((unsigned short *)icmp_hdr, sizeof(struct icmphdr));

    // 发送ICMP请求
    ssize_t bytes_sent = sendto(sockfd, buffer, sizeof(struct icmphdr), 0,
                                (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (bytes_sent < 0) {
        perror("sendto");
        exit(EXIT_FAILURE);
    }
}

// 接收ICMP响应
void receive_icmp_reply(int sockfd) {
    char buffer[BUFFER_SIZE];
    struct sockaddr_in server_addr;
    socklen_t server_addr_len = sizeof(server_addr);
    // 接收ICMP响应
    ssize_t bytes_received = recvfrom(sockfd, buffer, sizeof(buffer), 0,
                                      (struct sockaddr *)&server_addr, &server_addr_len);
    if (bytes_received < 0) {
        perror("recvfrom");
        exit(EXIT_FAILURE);
    }

    struct icmphdr *icmp_hdr = (struct icmphdr *)buffer;
    if (icmp_hdr->type == ICMP_ECHOREPLY) {
        char *time_str = buffer + sizeof(struct icmphdr);
        printf("Received time from server: %s\n", time_str);
    }
    else {
        printf("Received non-ECHOREPLY packet\n"); 
    }
}

// 计算ICMP校验和
unsigned short in_cksum(unsigned short *addr, int len) {
    register int nleft = len;
    register unsigned short *w = addr;
    register int sum = 0;
    unsigned short answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1) {
        unsigned short u = 0;
        *(unsigned char *)&u = *(unsigned char *)w;
        sum += u;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <server_ip>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sockfd;
    struct sockaddr_in server_addr;

    // 创建原始套接字
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    send_icmp_request(sockfd, server_addr);
    receive_icmp_reply(sockfd);

    close(sockfd);
    return 0;
}